///////////////////////////////////////////////////////////////////////////////
//
// This example demonstrates how to directly manipulate the pixels in the image buffer.
// It also gives an overview over the common color formats and how the pixels of each format 
// are organized in the buffer.
//
#define _WIN32_WINNT 0x0601

#include <conio.h>
#include <tisudshl.h>

#include "../Common/CmdHelper.h"

using namespace _DSHOWLIB_NAMESPACE;

static tFrameQueueBufferPtr	    getSingleFrame( Grabber& gr, const GUID& media_type )
{
    smart_ptr<FrameSnapSink> p_sink = FrameSnapSink::create( FrameTypeInfo( media_type ) );

    gr.setSinkType( p_sink );

    gr.startLive( false );
    Sleep( 250 ); // give the device time to adjust automatic settings i.e. auto exposure
    tFrameQueueBufferPtr img_ptr = p_sink->snapSingle( 2000 );
    gr.stopLive();

    gr.setSinkType( nullptr );

    // get pointer to the image data
    return img_ptr;
}

int main()
{
    // Initialize the library.
    InitLibrary();

    Grabber grabber;

    // Disable overlay. If it was in the graph, we could not get UYVY or 16-bit images.
    grabber.setOverlayBitmapPathPosition( ePP_NONE );

    if( !setupDeviceFromFile( grabber ) )
    {
        return -1;
    }

    {
        // *************************************************************************
        // image buffer pixel format is set to MEDIASUBTYPE_Y800

        tFrameQueueBufferPtr pActiveBuf = getSingleFrame( grabber, MEDIASUBTYPE_Y800 );

        //<<y800getptr
                // get pointer to the image data
        BYTE* pbImgData = pActiveBuf->getPtr();
        //>>

        //<<y800offset
                // Calculate the index of the upper left pixel
                // Images are stored top-down in the image buffer, so the first pixel has index 0.
        int iOffsUpperLeft = 0;
        //>>

        //<<y800print
        printf( "\nImage buffer pixel format is MEDIASUBTYPE_Y800\n" );
        printf( "Pixel 1: %d\n", pbImgData[iOffsUpperLeft] );
        printf( "Pixel 2: %d\n", pbImgData[iOffsUpperLeft + 1] );
        //>>

        //<<y800edit
                // overwrite the first 3 pixels and save image to disk
        pbImgData[iOffsUpperLeft] = 0;	// Set the first pixel to BLACK
        pbImgData[iOffsUpperLeft + 1] = 128;	// Set the second pixel to GRAY
        pbImgData[iOffsUpperLeft + 2] = 255;	// Set the third pixel to WHITE

        saveToFileBMP( *pActiveBuf, "Y800.bmp" );
        //>>		
        printf( "Image saved to file \"Y800.bmp\"\n" );
    }

    {
        // *************************************************************************
        // image buffer pixel format is set to eRGB24

        tFrameQueueBufferPtr pActiveBuf = getSingleFrame( grabber, MEDIASUBTYPE_RGB24 );

        //<<rgb24getptr
        struct RGB24Pixel {
            BYTE b;
            BYTE g;
            BYTE r;
        };
        RGB24Pixel* pImgData = (RGB24Pixel*)pActiveBuf->getPtr();
        //>>

        //<<rgb24offset
                // Calculate the index of the upper left pixel
                // Images are stored upside down in the image buffer
        SIZE dim = pActiveBuf->getFrameType().dim;
        int iOffsUpperLeft = (dim.cy - 1) * dim.cx;
        //>>

        //<<rgb24print
        printf( "\nImage buffer pixel format is MEDIASUBTYPE_RGB24\n" );
        // Please note: RGB values are stored in the following order: B,G,R
        printf( "Pixel 1(RGB): %d %d %d\n", pImgData[iOffsUpperLeft + 0].r,	//RED
                pImgData[iOffsUpperLeft + 0].g,	//GREEN
                pImgData[iOffsUpperLeft + 0].b );	//BLUE
        printf( "Pixel 2(RGB): %d %d %d\n", pImgData[iOffsUpperLeft + 1].r,	//RED
                pImgData[iOffsUpperLeft + 1].g,	//GREEN
                pImgData[iOffsUpperLeft + 1].b );	//BLUE
//>>

//<<rgb24edit
        // overwrite the first 3 pixels and save image to disk
        // Set the first pixel to RED
        pImgData[iOffsUpperLeft + 0].b = 0;	    // BLUE
        pImgData[iOffsUpperLeft + 0].g = 0;	    // GREEN
        pImgData[iOffsUpperLeft + 0].r = 255;	// RED

        // Set the second pixel to GREEN
        pImgData[iOffsUpperLeft + 1].b = 0;	    // BLUE
        pImgData[iOffsUpperLeft + 1].g = 255;	// GREEN
        pImgData[iOffsUpperLeft + 1].r = 0;	    // RED

        // Set the third pixel to BLUE
        pImgData[iOffsUpperLeft + 2].b = 255;	// BLUE
        pImgData[iOffsUpperLeft + 2].g = 0;	    // GREEN
        pImgData[iOffsUpperLeft + 2].r = 0;	    // RED

        saveToFileBMP( *pActiveBuf, "RGB24.bmp" );
        //>>
        printf( "Image saved to file \"RGB24.bmp\"\n" );
    }

    {
        // *************************************************************************
        // image buffer pixel format is set to eRGB32

        tFrameQueueBufferPtr pActiveBuf = getSingleFrame( grabber, MEDIASUBTYPE_RGB32 );

        //<<rgb32getptr
        RGBQUAD* pImgData = (RGBQUAD*)pActiveBuf->getPtr();
        //>>

        //<<rgb32offset
                // Calculate the index of the upper left pixel
                // Images are stored upside down in the image buffer
        SIZE dim = pActiveBuf->getFrameType().dim;
        int iOffsUpperLeft = (dim.cy - 1) * dim.cx;
        //>>

        //<<rgb32print
        printf( "\nImage buffer pixel format is MEDIASUBTYPE_RGB32\n" );
        printf( "Pixel 1(RGBA): %d %d %d %d\n", pImgData[iOffsUpperLeft + 0].rgbRed,
                pImgData[iOffsUpperLeft + 0].rgbGreen,
                pImgData[iOffsUpperLeft + 0].rgbBlue,
                pImgData[iOffsUpperLeft + 0].rgbReserved );
        printf( "Pixel 2(RGBA): %d %d %d %d\n", pImgData[iOffsUpperLeft + 1].rgbRed,
                pImgData[iOffsUpperLeft + 1].rgbGreen,
                pImgData[iOffsUpperLeft + 1].rgbBlue,
                pImgData[iOffsUpperLeft + 1].rgbReserved );
        //>>

        //<<rgb32edit
                // overwrite first 3 pixels and save image to disk
                // Set the first pixel to RED
        pImgData[iOffsUpperLeft + 0].rgbRed = 0xff;
        pImgData[iOffsUpperLeft + 0].rgbGreen = 0;
        pImgData[iOffsUpperLeft + 0].rgbBlue = 0;
        pImgData[iOffsUpperLeft + 0].rgbReserved = 0;

        // Set the second pixel to GREEN
        pImgData[iOffsUpperLeft + 1].rgbRed = 0;
        pImgData[iOffsUpperLeft + 1].rgbGreen = 0xff;
        pImgData[iOffsUpperLeft + 1].rgbBlue = 0;
        pImgData[iOffsUpperLeft + 1].rgbReserved = 0;

        // Set the third pixel to BLUE
        pImgData[iOffsUpperLeft + 2].rgbRed = 0;
        pImgData[iOffsUpperLeft + 2].rgbGreen = 0;
        pImgData[iOffsUpperLeft + 2].rgbBlue = 0xff;
        pImgData[iOffsUpperLeft + 2].rgbReserved = 0;

        saveToFileBMP( *pActiveBuf, "RGB32.bmp" );
        //>>
        printf( "Image saved to file \"RGB32.bmp\"\n" );
    }
    /*********************************************************************************/

    // If the source video format is UYVY, we can grab it.
    if( grabber.getVideoFormat().getSubtype() == MEDIASUBTYPE_UYVY )
    {
        // *************************************************************************
        // image buffer pixel format is set to eUYVY

        tFrameQueueBufferPtr pActiveBuf = getSingleFrame( grabber, MEDIASUBTYPE_UYVY );

        //<<uyvygetptr
                // UYVY Images are stored top-down, so the upper left pixel starts at byte 0.
                // Each 4 bytes represent the color for 2 neighboring pixels:
                // 
                // [ U0 | Y0 | V0 | Y1 ]
                //
                // Y0 is the brightness of pixel 0, Y1 the brightness of pixel 1.
                // U0 and V0 is the color of both pixels.
        struct UYVYQuad
        {
            BYTE U0;
            BYTE Y0;
            BYTE V0;
            BYTE Y1;
        };
        UYVYQuad* pImgData = (UYVYQuad*)pActiveBuf->getPtr();
        //>>

        //<<uyvyoffset
                // Calculate the index of the upper left pixel
                // Images are stored top-down in the image buffer, so the first two pixels have index 0.
        int iOffsUpperLeft = 0;
        //>>

        //<<uyvyprint
        printf( "\nImage buffer pixel format is MEDIASUBTYPE_UYVY\n" );
        printf( "Pixel 1 (Y U V): %d %d %d\n", 
                pImgData[iOffsUpperLeft + 0].Y0,
                pImgData[iOffsUpperLeft + 0].U0,
                pImgData[iOffsUpperLeft + 0].V0 );
        printf( "Pixel 2 (Y U V): %d %d %d\n",
                pImgData[iOffsUpperLeft + 0].Y1,
                pImgData[iOffsUpperLeft + 0].U0,
                pImgData[iOffsUpperLeft + 0].V0 );
        printf( "Pixel 3 (Y U V): %d %d %d\n",
                pImgData[iOffsUpperLeft + 1].Y0,
                pImgData[iOffsUpperLeft + 1].U0,
                pImgData[iOffsUpperLeft + 1].V0 );
        printf( "Pixel 4 (Y U V): %d %d %d\n",
                pImgData[iOffsUpperLeft + 1].Y1,
                pImgData[iOffsUpperLeft + 1].U0,
                pImgData[iOffsUpperLeft + 1].V0 );
        //>>

        //<<uyvyedit
                // Overwrite the first 6 pixels and save image to disk
                // set the first 2 pixels to RED
        pImgData[iOffsUpperLeft + 0].U0 = 90;
        pImgData[iOffsUpperLeft + 0].V0 = 240;
        pImgData[iOffsUpperLeft + 0].Y0 = 82;
        pImgData[iOffsUpperLeft + 0].Y1 = 164;	// Make the 2nd pixel brighter

        // set the 3rd and 4th to GREEN
        pImgData[iOffsUpperLeft + 1].U0 = 54;
        pImgData[iOffsUpperLeft + 1].V0 = 34;
        pImgData[iOffsUpperLeft + 1].Y0 = 145;
        pImgData[iOffsUpperLeft + 1].Y1 = 72;	// Make the 2nd pixel darker

        // set the 5th and 6th pixel to BLUE
        pImgData[iOffsUpperLeft + 2].U0 = 240;
        pImgData[iOffsUpperLeft + 2].V0 = 110;
        pImgData[iOffsUpperLeft + 2].Y0 = 41;
        pImgData[iOffsUpperLeft + 2].Y1 = 82;	// Make the 2nd pixel brighter

        // The values above were calculated using the following formulas:
        //
        //	Y  =  (0.257 * R) + (0.504 * G) + (0.098 * B) + 16
        //  V  =  (0.439 * R) - (0.368 * G) - (0.071 * B) + 128
        //  U  = -(0.148 * R) - (0.291 * G) + (0.439 * B) + 128
        //

        // We save the buffer as RGB24, so that other software can view it.
        saveToFileBMP( *pActiveBuf, "UYVY.bmp", eRGB24 );
        //>>
        printf( "Image saved to file \"UYVY.bmp\"\n" );
    }
    else
    {
        printf( "\nTo show the UYVY pixel format, select a UYVY video format\n" );
    }
    /*********************************************************************************/

    // If the source video format is Y16 (so a 16 bit gray format), we can grab it.
    if( grabber.getVideoFormat().getSubtype() == MEDIASUBTYPE_Y16 )
    {
        // *************************************************************************
        // image buffer pixel format is set to Y16

        tFrameQueueBufferPtr pActiveBuf = getSingleFrame( grabber, MEDIASUBTYPE_Y16 );

        //<<y16getptr
        // Y16 Images are stored top-down, so the upper left pixel starts at byte 0.
        // 2 Byte represent one pixel.
        uint16_t* pImgData = (uint16_t*)pActiveBuf->getPtr();
        //>>

        //<<y16print
        // The Y16 pixel values exactly map unsigned 16-bit integers:
        uint16_t val0 = pImgData[0];
        uint16_t val1 = pImgData[1];

        printf( "\nImage buffer pixel format is eY16\n" );
        printf( "Pixel 1 (Y16): %d\n", val0 );
        printf( "Pixel 2 (Y16): %d\n", val1 );
        //>>

        //<<y16edit
        pImgData[0] = 0;		// Set the first pixel to BLACK
        pImgData[1] = 32768;	// Set the second pixel to GRAY
        pImgData[2] = 65535;	// Set the third pixel to WHITE

        // We save the buffer as TIFF, so that other software can view it.
        saveToFileTIFF( *pActiveBuf, "Y16.tiff" );
        //>>
        printf( "Image saved to file \"Y16.tiff\"\n" );
    }
    else
    {
        printf( "\nTo show the Y16 pixel format, select a Y16 video format\n" );
    }
    /*********************************************************************************/

    // If the source video format is RGB64 (so a 64 bit color format), we can grab it.
    if( grabber.getVideoFormat().getSubtype() == MEDIASUBTYPE_RGB64 )
    {
        // *************************************************************************
        // image buffer pixel format is set to RGB64

        tFrameQueueBufferPtr pActiveBuf = getSingleFrame( grabber, MEDIASUBTYPE_RGB64 );

        //<<rgb64getptr
        struct RGB64Pixel
        {
            uint16_t b;
            uint16_t g;
            uint16_t r;
            uint16_t reserved;
        };

        RGB64Pixel* pImgData = (RGB64Pixel*)pActiveBuf->getPtr();
        //>>

        //<<rgb64offset
                // Calculate the index of the upper left pixel
                // Images are stored upside down in the image buffer
        SIZE dim = pActiveBuf->getFrameType().dim;
        int offset_upper_left = (dim.cy - 1) * dim.cx;
        //>>

        //<<rgb64print
        // The RGB64 pixel values exactly map unsigned 16-bit integers:
        RGB64Pixel pix0 = pImgData[offset_upper_left + 0];
        RGB64Pixel pix1 = pImgData[offset_upper_left + 1];

        printf( "\nImage buffer pixel format is MEDIASUBTYPE_RGB64\n" );
        printf( "Pixel 1(RGBA): %d %d %d %d\n",
                pix0.r,
                pix0.g,
                pix0.b,
                pix0.reserved );
        printf( "Pixel 2(RGBA): %d %d %d %d\n",
                pix1.r,
                pix1.g,
                pix1.b,
                pix1.reserved );
        //>>


        //<<rgb64edit
        // overwrite first 3 pixels and save image to disk
        // Set the first pixel to RED
        pImgData[offset_upper_left + 0].r = 0xffFF;
        pImgData[offset_upper_left + 0].g = 0;
        pImgData[offset_upper_left + 0].b = 0;
        pImgData[offset_upper_left + 0].reserved = 0;

        // Set the second pixel to GREEN
        pImgData[offset_upper_left + 1].r = 0;
        pImgData[offset_upper_left + 1].g = 0xffFF;
        pImgData[offset_upper_left + 1].b = 0;
        pImgData[offset_upper_left + 1].reserved = 0;

        // Set the third pixel to BLUE
        pImgData[offset_upper_left + 2].r = 0;
        pImgData[offset_upper_left + 2].g = 0;
        pImgData[offset_upper_left + 2].b = 0xfFFf;
        pImgData[offset_upper_left + 2].reserved = 0;

        // We save the buffer as TIFF, so that other software can view it.
        saveToFileTIFF( *pActiveBuf, "rgb64.tiff" );
        //>>
        printf( "Image saved to file \"rgb64.tiff\"\n" );
    }
    else
    {
        printf( "\nTo show the RGB64 pixel format, select a RGB64 video format\n" );
    }
    /*********************************************************************************/

    printf( "Press any key to continue." );
    _getch();

    return 0;
}
